package com.mathdroid.gui.main.browser;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.LinkedList;

import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JToolTip;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;

import com.mathdroid.gui.ImageLoader;
import com.mathdroid.gui.main.ItemToolTip;
import com.mathdroid.gui.main.character.ItemSlotPopup;
import com.mathdroid.model.EModelObserverEvent;
import com.mathdroid.model.IModelObserver;
import com.mathdroid.model.Model;
import com.mathdroid.model.enums.EItemSlot;
import com.mathdroid.model.items.Item;
import com.mathdroid.model.items.ItemMap;

/**
 * The item browser panel. This is the panel that shows the bars and items that
 * passed all filters.
 */
public final class ItemBrowser extends JPanel implements IModelObserver {

	/**
	 * Default UID.
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * The parent of this panel.
	 */
	private JScrollPane scrollpane = null;
	
	/**
	 * The space between the parents viewport and the inner border of this
	 * panel.
	 */
	private static final int BORDER_PADDING = 10;
	
	/**
	 * The space around and between item bars.
	 */
	private static final int SPACING = 5;
	
	/**
	 * The height of an item bar.
	 */
	private static final int BAR_HEIGHT = 30;
	
	/**
	 * The height of the item icon.
	 */
	private static final int ICON_HEIGHT = 30;
	
	/**
	 * The width of the item icon.
	 */
	private static final int ICON_WIDTH = 30;
	
	/**
	 * The items that are shown in the panel.
	 */
	private LinkedList<Item> shownItems = new LinkedList<Item>();
	
	/**
	 * The rating of the best item.
	 */
	private double maxRating = 0;
	
	/**
	 * The bounds of this panel.
	 */
	private Dimension bounds = new Dimension();
	
	/**
	 * The yellow-green bar color.
	 */
	private static final Color DROID_GREEN = new Color(172, 230, 52);
	
	/**
	 * The areas that respond to mouse events.
	 */
	private final LinkedList<Rectangle> iconArea = new LinkedList<Rectangle>();
	
	/**
	 * The tooltip used in this browser.
	 */
	private ItemToolTip tip = null;
	
	/**
	 * Image loader utility.
	 */
	private ImageLoader il = new ImageLoader();
	
	/**
	 * The horizontal offset of the tooltip.
	 */
	private static final int TOOL_TIP_HORIZONTAL_OFFSET = 20;
	
	/**
	 * Create a new item browser.
	 * @param p The popup parent component. Can be null.
	 * @param slot The slot to put the module in, if popup is not null.
	 */
	public ItemBrowser(final ItemSlotPopup p, final EItemSlot slot) {
		super();
		Model.registerObserver(this);
		this.setToolTipText("");
		
		this.addMouseListener(new BrowserMouseListener(
				this.iconArea, this.shownItems, p, slot));
	}
	
	@Override
	public JToolTip createToolTip() {
		if (this.tip == null) {
			this.tip = new ItemToolTip();
			this.tip.setComponent(this);
		}
		return this.tip;
	}

	@Override
	public String getToolTipText(final MouseEvent e) {
		final int x = e.getX();
		final int y = e.getY();
		int i = 0;
		String s = "";
		ToolTipManager.sharedInstance().setEnabled(false);
		
		for (Rectangle r : iconArea) {
			if (x > r.getX() && x < r.getX() + r.getWidth()
					&& y > r.getY() && y < r.getY() + r.getHeight()) {
				Item item = shownItems.get(i);
				
				switch (item.getType()) {
					case WEAPON:
						s = item.getWeaponProficiency().toString();
						break;
					case ARMOR:
						s = item.getArmorProficiency().toString();
						break;
					case MODULE:
						s = item.getModuleProficiency().toString();
						break;
					default:
						s = "Unknown";
						break;
				}
				
				if (this.tip != null) {
					this.tip.setItem(item);
				}
				ToolTipManager.sharedInstance().setEnabled(true);
				break;
			}
			i++;
		}
		
		return s;
	}
	
	@Override
	public Point getToolTipLocation(final MouseEvent e) {
		Point p = e.getPoint();
		p.x += TOOL_TIP_HORIZONTAL_OFFSET;
		
		/* (Almost) prevent flicker by painting the ToolTip inside the panel. */
//		if (this.tip != null) {
//			
//			final int width = (int) this.tip.getPreferredSize().getWidth();
//			final int height = (int) this.tip.getPreferredSize().getHeight();
//			final int pWidth = (int) this.bounds.getWidth();
//			final int pHeight = (int) this.bounds.getHeight();
//			
//			if (p.x + width > pWidth) {
//				p.x = e.getPoint().x;
//				p.x = pWidth - SPACING - width;
//			}
//			
//			if (p.y + height > pHeight) {
//				p.y = e.getPoint().y;
//				p.y = pHeight - SPACING - height;
//			}
//		}
		
		return p;
	 }
	
	/**
	 * Sets the parent scroll pane of this panel.
	 * @param p Parent scroll pane.
	 */
	public void setParentScrollPane(final JScrollPane p) {
		this.scrollpane = p;
	}
	
	/**
	 * Refresh the items in the list.
	 */
	public void refresh() {
		this.shownItems.clear();
		ItemMap items = Model.getProfile().getItemBrowserModel()
								.getFilteredItems();
		this.shownItems.addAll(items.values());
		Collections.sort(this.shownItems, new RatingComparator<Item>());
		this.maxRating = this.getMaxRating();
	}

	@Override
	public void paintComponent(final Graphics g) {
		this.bounds = this.calcPreferredDimension(this.shownItems.size());
		
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, this.bounds.width, this.bounds.height);
		
		g.setColor(Color.GRAY);
		g.drawRect(BORDER_PADDING, BORDER_PADDING, 
				this.bounds.width - 2 * BORDER_PADDING, 
				this.bounds.height - 2 * BORDER_PADDING);
		
		final int maxBarWidth = 
			this.bounds.width - 2 * BORDER_PADDING - 3 * SPACING - ICON_WIDTH;
		int width = 0;
		
		int row = 0;
		this.iconArea.clear();
		for (Item i : this.shownItems) {
			width = (int) (maxBarWidth * i.getRating() / this.maxRating);
			
			g.setColor(DROID_GREEN);
			g.fillRect(BORDER_PADDING + 2 * SPACING + ICON_WIDTH, 
					row * (BAR_HEIGHT + SPACING) + (BORDER_PADDING + SPACING), 
					width, BAR_HEIGHT);
			
			Rectangle r = new Rectangle(
					BORDER_PADDING + SPACING, 
					row * (BAR_HEIGHT + SPACING) + (BORDER_PADDING + SPACING), 
					ICON_WIDTH, 
					ICON_HEIGHT);
			
			String star = "nostar";
			if (i.isAvailable()) {
				star = "star";
			}
			
			g.drawImage(this.il.getImage(star).getImage(), 
					(int) r.getX() + ICON_WIDTH, 
					(int) r.getY(), null);
			
			g.setColor(Color.BLACK);
			g.drawString(i.getName(), 
					BORDER_PADDING + 2 * SPACING + 2 * ICON_WIDTH, 
					row * (BAR_HEIGHT + SPACING) 
					+ (BORDER_PADDING + SPACING) + BAR_HEIGHT - SPACING);
			
			this.iconArea.add(r);
			g.drawImage(this.il.getItemIcon(i.getImageName()).getImage(), 
							(int) r.getX(), 
							(int) r.getY(),
					ICON_WIDTH, ICON_HEIGHT, null);
			
			row++;
		}
	}
	
	/**
	 * Get the highest rating of the items.
	 * @return Highest rating.
	 */
	private double getMaxRating() {
		double max = 0;
		double temp = 0;
		
		for (Item i : this.shownItems) {
			temp = i.getRating();
			if (temp > max) {
				max = temp;
			}
		}
		
		return max;
	}
	
	@Override
	public Dimension getPreferredSize() {
		return this.calcPreferredDimension(this.shownItems.size());
	}
	
	/**
	 * Calculate the necessary dimensions of the item browser.
	 * @param items Amount of items.
	 * @return Minimal dimension.
	 */
	public Dimension calcPreferredDimension(final int items) {
		int width = this.scrollpane.getViewportBorderBounds().width;
		int vpHeight = this.scrollpane.getViewportBorderBounds().height;
		int height = BORDER_PADDING * 2 
					+ SPACING 
					+ items * (BAR_HEIGHT + SPACING);
		
		if (height < vpHeight) {
			height = vpHeight;
		}
		
		final Dimension d = new Dimension(width, height);
		this.setPreferredSize(d);
		return d;
	}

	@Override
	public void modelChanged(final EModelObserverEvent e) {
		if (e == EModelObserverEvent.PROFILE_LOADED 
				|| e == EModelObserverEvent.ITEM_BROWSER_MODEL_CHANGED) {
			this.refresh();
			this.scrollpane.setViewportView(this);
			this.repaint();
		}
	}
}
